// Copyright (c) 2010 Omer Goshen
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#ifndef __PROPERTY_H__
#define __PROPERTY_H__

#include <Ogre.h>

#include <iostream>
#include <string>
#include <typeinfo>

#include <sigc++/sigc++.h>
#include <sigc++/signal.h>
#include <sigc++/signal_base.h>

#include "tinyxml.h"

#include "Value.h"
#include "Serializable.h"

class IProperty;
class Object;
class Function;

//{{{ IProperty
/**
 * \brief Property interface
 */
class IProperty : public Serializable
{
public:
	virtual ~IProperty() {}

	virtual std::string getName() = 0;
	virtual std::string getTypeName() = 0;

	virtual bool isNull() = 0;

	virtual const std::type_info &type_info() const = 0;
	virtual void* _getValuePtr() = 0;

	virtual std::string getXmlString() = 0;
//private:
//	IProperty() {}
};
//}}}


template<class T>
/**
 * \class Property
 * \author gershon
 * \brief Type safe templated property
 */
class Property : public IProperty
{

public:
	explicit Property(const std::string& n) : mName(n), mTypeName(_setTypeName()) {}
	Property(const std::string& n, const T& v) : mValue(v), mName(n), mTypeName(_setTypeName()) {}

//	explicit Property(const std::string& n) : mName(n) { _setTypeName(); }
//	Property(const std::string& n, const T& v) : mValue(v), mName(n) { _setTypeName(); }

	virtual ~Property() {}

	virtual std::string getName() { return mName; }
//	virtual const std::string& getTypeName() const { return mTypeName; }
	virtual std::string getTypeName() { return mTypeName; }
	
	const T& Get() const { return mValue; }
	operator T() const { return mValue; }
	
	const T& Set(const T& v)
	{
		if(mValue==v) return mValue;
		mValue = v;
		onChanged.emit();
		return mValue;
	}
	
	// Doesn't fire the onChanged event
	const T& RawSet(const T& v)
	{
		if(mValue==v) return mValue;
		mValue = v;
		return mValue;		
	}
	
	// Assign Property
	inline Property<T> operator= (const Property<T> &p) { return Set(*p); }

	// Assign value
	inline const T& operator= (const T& v)
	{
		if(mValue==v) return mValue;
		mValue = v;
		onChanged.emit();
		return mValue;
	};

//	inline const std::string& getXmlString() const {
	/**
	 * \brief The xml representation of this property
	 * Example ouput: <property type="FLOAT" name="mass" data="1"/>
	 */
	inline std::string getXmlString()
	{

		std::stringstream ss;

		std::string typeName = getTypeName();
		std::transform(typeName.begin(), typeName.end(), typeName.begin(), (int (*)(int))std::toupper);

		ss << "<Property ";
		ss << "type=\"" << typeName << "\" ";
		ss << "name=\"" << getName() << "\" ";
		ss << "value=\"" << mValue << "\" ";
		ss << "/>";

		return ss.str();
	}


	/**
	 * \brief
	 * \return standard type_info
	 */
	inline const std::type_info &type_info() const { return typeid(mValue);	}


	inline bool isNull() { return &mValue==NULL; }
	/**
	 * \return A generic pointer to the internal value
	 */
	inline void* _getValuePtr() { return (void*)&mValue; }

//	boost::signal<void ()> onChanged;
	sigc::signal<void> onChanged;

private:
	// force named contruction only...
	Property() {}
//	bool dirty;

	inline const char* _setTypeName() const	{ return TypeInfo::getTypeName<T>(); }

protected:
	/** Internal value **/
	T mValue;
	std::string const mName;
	std::string const mTypeName;
};



// Casting
template<typename T>
Property<T>* prop_cast(void *p)
{
	return reinterpret_cast< Property<T>* >(p);
}


template<typename T>
Property<T> prop_cast_val(void *p)
{
	return *(reinterpret_cast< Property<T>* >(p));
}

namespace PropertyDeserializer
{
	IProperty* fromXmlString(const char* xml);
}


#endif
